package com.farsunset.cim.sdk.server.slc.handler.udp;

import com.farsunset.cim.acceptor.config.SocketConfig;
import com.farsunset.cim.coder.protobuf.AppMessageDecoder;
import com.farsunset.cim.coder.protobuf.AppMessageEncoder;
import com.farsunset.cim.sdk.server.slc.handler.BaseNioSocketAcceptor;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * @author slc
 * @date 2022/07/12 下午 3:05
 */
public class UdpNioSocketAcceptor extends BaseNioSocketAcceptor {

    private final EventLoopGroup bossGroup;

    protected UdpNioSocketAcceptor(SocketConfig socketConfig, boolean usePlatformOptimization) {
        super(socketConfig, usePlatformOptimization);

        ThreadFactory bossThreadFactory = r -> {
            Thread thread = new Thread(r);
            thread.setName(getBossThreadName() + thread.getId());
            return thread;
        };

        if (isLinuxSystem()) {
            bossGroup = new EpollEventLoopGroup(bossThreadFactory);
        } else {
            bossGroup = new NioEventLoopGroup(bossThreadFactory);
        }

    }

    /**
     * 执行启动SOCKET服务
     */
    public void bind() {
        if (!socketConfig.isEnable()) {
            return;
        }

        Bootstrap bootstrap = createBootstrap();
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) {
                ch.pipeline().addLast(blacklistHandler);
                ch.pipeline().addLast(new AppMessageDecoder());
                ch.pipeline().addLast(new AppMessageEncoder());
                ch.pipeline().addLast(socketConfig.getLoggingHandler() == null ? defaultLoggingHandler : socketConfig.getLoggingHandler());
                ch.pipeline().addLast(new IdleStateHandler(socketConfig.getReadIdle().getSeconds(), socketConfig.getWriteIdle().getSeconds(), 0, TimeUnit.SECONDS));
                ch.pipeline().addLast(UdpNioSocketAcceptor.this);
            }
        });

        ChannelFuture channelFuture = bootstrap.bind(socketConfig.getPort()).syncUninterruptibly();
        channelFuture.channel().newSucceededFuture().addListener(future -> {
            String logBanner = "\n\n" +
                    "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n" +
                    "*                                                                                   *\n" +
                    "*                                                                                   *\n" +
                    "*                  App Udp Socket Server started on port {}.                      *\n" +
                    "*                                                                                   *\n" +
                    "*                                                                                   *\n" +
                    "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n";
            logger.info(logBanner, socketConfig.getPort());
        });
        channelFuture.channel().closeFuture().addListener(future -> this.destroy());
    }


    /**
     * 执行注销SOCKET服务
     */
    public void destroy() {
        if (bossGroup != null && !bossGroup.isShuttingDown() && !bossGroup.isShutdown()) {
            try {
                bossGroup.shutdownGracefully();
            } catch (Exception ignore) {
            }
        }

    }

    protected Bootstrap createBootstrap() {
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(bossGroup).channel(isLinuxSystem() ? EpollDatagramChannel.class : NioDatagramChannel.class)
                .option(ChannelOption.SO_BROADCAST, true);
        return bootstrap;
    }


    protected String getBossThreadName() {
        return "nio-udp-boss-";
    }

}
